#include <search.h>
#include <book.h>
#include <game.h>
#include <movegen.h>
#include <eval.h>
#include <san.h>
#include <stats.h>
#include <ttable.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/time.h>
#include <setjmp.h>


#define TIME_POLLING_MASK 0xff
#define IO_POLLING_MASK 0xfff
#define MAX_PV_LINE_LEN MAX_DEPTH
#define MAX_TIME 7200000  // milliseconds, max 2 hours per move
#define ASPIRATION_WINDOW 1000

#define RESIGNATION_SCORE 600  // to be moved elsewhere
#define RESIGNATION_MOVES 4

/*
 * TODO:
 * - refine the time allocation strategy
 * - promotions (to queen) in qsearch
 * - setjmp in absearch @ depth == 0 to save a partial search
 * - verify the order in which the null move must be tried
 * - review aspirated search
 * - move resignation constants elsewhere (interface?)
 */

static const int futility_margin = 200;


static move_t oldpv[MAX_PV_LINE_LEN];
static int oldpvlen;
static move_t pvlines[MAX_DEPTH + 1][MAX_PV_LINE_LEN];
static int pvlen[MAX_DEPTH + 1];

move_t killers[MAX_DEPTH + 1][2];
static volatile enum {
  cont_flag,
  hurry_flag,
  cancel_flag
} stop_flag;
static uint64_t nodes;
interface_t* interface_ptr;
static struct timeval timeout;
jmp_buf jmp_stopsearch;


static int check_timeout()
{
  struct timeval now;
  gettimeofday(&now, 0);
  long diff = 1000000 * (now.tv_sec - timeout.tv_sec);
  diff += now.tv_usec - timeout.tv_usec;
  return (diff >= 0);
}


static void update_pvline(int depth, move_t m)
{
  assert(depth >= 0);
  assert(depth <= MAX_DEPTH);
  assert(stop_flag == cont_flag);
  if (depth == MAX_DEPTH) {
    pvlen[depth] = 0;
    return;
  }
  int n = pvlen[depth + 1] + 1;
  if (n > MAX_PV_LINE_LEN)
    n = MAX_PV_LINE_LEN;
  pvlen[depth] = n;
  pvlines[depth][0] = m;

  memcpy(pvlines[depth] + 1,
	 pvlines[depth + 1],
	 sizeof(move_t) * (n - 1));
}


static int null_move_enabled(const game_t* g)
{
  assert(g != 0);

  /* Null move is disabled in endgame.  Here this means that there
     must be one knight, bishop, rook, or queen for the side to
     move.*/

  const position_t* pos = game_cur_position(g);
  int stm = position_get_stm(pos);

  if (!(BB_POS_PIECES(pos, stm, KNIGHT) |
  	BB_POS_PIECES(pos, stm, BISHOP) |
  	BB_POS_PIECES(pos, stm, ROOK) |
  	BB_POS_PIECES(pos, stm, QUEEN)))
    return 0;

  if (game_last_move(g) == NULL_MOVE)
    return 0;

  return 1;
}


static void pop_best_move(move_t* buf, int buflen)
{
  if (buflen == 0)
    return;

  int v = MOVE_GET_EXTRA(buf[0]);
  int b = 0;
  for (int i = 1; i < buflen; i++) {
    int vv = MOVE_GET_EXTRA(buf[i]);
    if (vv > v) {
      v = vv;
      b = i;
    }
  }
  move_t tmp = buf[0];
  buf[0] = buf[b];
  buf[b] = tmp;
  MOVE_CLEAR_EXTRA(buf[0]);
}


static int delete_move(move_t m, move_t* buf, int buflen)
{
  assert(buflen >= 0);
  assert(buf != 0);

  for (int i = 0; i < buflen; i++) {
    if (buf[i] == m) {
      --buflen;
      buf[i] = buf[buflen];
      break;
    }
  }
  return buflen;
}


static void evaluate_moves(const position_t* pos,
			   move_t* buf,
			   int buflen)
{
  assert(buflen >= 0);

  static const int pvalues[7] = {
    100, 300, 310, 500, 900, 0, 0
  };

  for (int i = 0; i < buflen; i++) {
    int from = MOVE_FROM(buf[i]);
    int to = MOVE_TO(buf[i]);
    int v = 2048;
    v += pvalues[position_get_square(pos, to)];
    v -= pvalues[position_get_square(pos, from)];
    MOVE_SET_EXTRA(buf[i], v);
  }
}


static void evaluate_moves_see(const position_t* pos,
			       move_t* buf,
			       int buflen)
{
  assert(buflen >= 0);

  for (int i = 0; i < buflen; i++) {
    int v = 2048 + movegen_see(pos, buf[i]);
    if (position_get_square(pos, MOVE_TO(buf[i])) != EMPTY)
      v += 20;  // small bonus for captures
    MOVE_SET_EXTRA(buf[i], v);
  }
}


static void evaluate_killers(int depth, move_t* buf, int buflen)
{
  assert(buflen >= 0);
  assert(depth >= 0);
  assert(depth <= MAX_DEPTH);

  static const int killer_val[2] = {10, 5};

  for (int i = 0; i < buflen; i++) {
    move_t m = buf[i];
    MOVE_CLEAR_EXTRA(m);
    if (m == killers[depth][0])
      MOVE_SET_EXTRA(buf[i], MOVE_GET_EXTRA(buf[i]) + killer_val[0]);
    else if (m == killers[depth][1])
      MOVE_SET_EXTRA(buf[i], MOVE_GET_EXTRA(buf[i]) + killer_val[1]);
  }
}


static int qsearch(game_t* g,
		   int alpha,
		   int beta,
		   int depth)
{
  const position_t* pos = game_cur_position(g);

  int score = eval(pos, alpha, beta);

  if (depth == MAX_DEPTH || score >= beta)
    return score;
  if (score > alpha)
    alpha = score;

  int check = game_in_check(g);

  move_t movebuf[MAX_N_MOVES];
  int n;
  if (check) {
    n = movegen_check_evasion(pos, movebuf);
    evaluate_moves(pos, movebuf, n);
  } else {
    n = movegen_captures(pos, movebuf);
    n += movegen_queen_promotions(pos, movebuf + n);
    evaluate_moves(pos, movebuf, n); // use see?
  }

  int legal = 0;
  for (int i = 0; i < n; i++) {
    pop_best_move(movebuf + i, n - i);
    if (!movegen_verify_move(pos, movebuf[i]))
      continue;
    ++legal;
    int captured = position_get_square(pos, MOVE_TO(movebuf[i]));
    if (score + pieces_values[captured] + 100 <= alpha)  // !!! to be tuned
      continue;
    if (score + movegen_see(pos, movebuf[i]) + 100 <= alpha)  // !!! to be tuned
      continue;

    game_make_move(g, movebuf[i]);
    int v = -qsearch(g, -beta, -alpha, depth + 1);
    game_undo_move(g);
    if (v <= score)
      continue;
    score = v;
    if (score >= beta)
      break;
    if (score > alpha)
      alpha = score;
  }
  if (check != 0 && legal == 0)
    return -MATE_VAL(depth);
  return score;
}


static void evaluate_moves_qsearch(game_t* g,
				   move_t* buf,
				   int buflen)
{
  assert(buflen >= 0);
  const position_t* pos = game_cur_position(g);

  for (int i = 0; i < buflen; i++) {
    int v = 0;
    if (movegen_verify_move(pos, buf[i])) {
      game_make_move(g, buf[i]);
      v = -qsearch(g, -INF_VAL, INF_VAL, 0);
      game_undo_move(g);
      v += 2048;
      if (v < 0)
	v = 0;
      else if (v > 4095)
	v = 4095;
    }
    MOVE_SET_EXTRA(buf[i], v);
  }
}


static void add_ttable_entry(const position_t* pos,
			     move_t best_move,
			     int score,
			     int depth,
			     int init_maxdepth,
			     int init_alpha,
			     int beta)
{
  ttable_entry_t ttentry;
  ttentry.best = best_move;
  if (IS_MATE_VAL(score)) {
    assert(depth <= MATE_DEPTH(score));
    ttentry.score = MATE_VAL(MATE_DEPTH(score) - depth);
    ttentry.depth = MAX_DEPTH;
  } else if (IS_MATE_VAL(-score)) {
    assert(depth <= MATE_DEPTH(-score));
    ttentry.score = -MATE_VAL(MATE_DEPTH(-score) - depth);
    ttentry.depth = MAX_DEPTH;
  } else {
    ttentry.score = score;
    ttentry.depth = init_maxdepth - depth;
  }

  if (score <= init_alpha)
    ttentry.flags = TT_ALPHA;
  else if (score >= beta)
    ttentry.flags = TT_BETA;
  else
    ttentry.flags = TT_EXACT;

  ttable_insert(pos, &ttentry);
}


static int absearch(game_t* g,
		    int alpha,
		    int beta,
		    int depth,
		    int maxdepth,
		    int inpv)
{
  assert(g != 0);
  assert(depth >= 0);
  assert(alpha <= beta);
  assert(maxdepth >= 0);
  assert(depth <= maxdepth);
  assert(maxdepth <= MAX_DEPTH);

  ++nodes;
  STAT_REPORT(stat_nodes);

  // 1) I/O, time, and interruption check.
  if (stop_flag != cont_flag) {
    STAT_REPORT(stat_interrupted_nodes);
    longjmp(jmp_stopsearch, 1);
  }

  if (!(nodes & TIME_POLLING_MASK) && check_timeout()) {
    stop_flag = hurry_flag;
    STAT_REPORT(stat_interrupted_nodes);
    longjmp(jmp_stopsearch, 1);
  }

  if (!(nodes & IO_POLLING_MASK))
    interface_handle_input(interface_ptr);

  // 2) If leaf then qsearch.
  pvlen[depth] = 0;
  if (depth == maxdepth) {
    STAT_REPORT(stat_terminal_nodes);
    return qsearch(g, alpha, beta, depth);
  }

  STAT_REPORT(stat_nonterminal_nodes);

  // 2a) If repeated return draw.
  if (depth > 0 && game_repeated_position(g)) {
    STAT_REPORT(stat_interior_nodes);
    return 0;
  }

  // 2b) Draw by material or by 50 moves.
  if (depth > 0 && (game_draw_by_material(g) ||
		    game_draw_by_50(g))) {
    STAT_REPORT(stat_interior_nodes);
    return 0;
  }

  // 2c) Mate distance pruning
  if (IS_MATE_VAL(alpha) && MATE_DEPTH(alpha) <= depth) {
    STAT_REPORT(stat_alpha_nodes);
    return alpha;
  } else if (IS_MATE_VAL(-beta) && MATE_DEPTH(-beta) <= depth) {
    STAT_REPORT(stat_beta_nodes);
    return beta;
  }

  // 3) Init search data.
  const position_t* pos = game_cur_position(g);
  int legal = 0;
  int search_pv = 1;
  int score = -INF_VAL;
  int init_maxdepth = maxdepth;
  int init_alpha = alpha;
  move_t pv_move = NULL_MOVE;
  move_t table_move = NULL_MOVE;
  move_t iid_move = NULL_MOVE;
  move_t best_move = NULL_MOVE;
  ttable_entry_t ttentry;
  int check = game_in_check(g);
  
  // 4) Extensions.
  if (maxdepth < MAX_DEPTH) {
    if (check ||
	(MOVE_PROMOTION(game_last_move(g)) != NO_PROMOTION)) {
      ++maxdepth;
      STAT_REPORT(stat_extended_nodes);
    }
  }

  // 4b) Futility pruning.
  if (depth == maxdepth - 1) {
    int target = alpha - futility_margin;
    int v = eval(pos, target -1, target);
    if (v  < target) {
      v = qsearch(g, alpha, alpha + 1, depth);
      if (v <= alpha) {
	STAT_REPORT(stat_futility_pruning);
	return v;
      }
    }
  }

  /*
  // 4c) Razoring.
  if (depth == maxdepth - 2) {
    int v = eval(pos);
    if (v + futility_margin <= alpha) {
      v = qsearch(g, alpha, beta, depth);
      if (v <= alpha)
	return v;
    }
  }
  */

  // 5) If in pv try the pv move of the previous iteration.
  if (inpv && depth < oldpvlen) {
    pv_move = oldpv[depth];
    assert(movegen_is_pseudo_legal(pos, pv_move));
    assert(movegen_verify_move(pos, pv_move));
    game_make_move(g, pv_move);
    int v = -absearch(g, -beta, -alpha, depth + 1, maxdepth, 1);
    game_undo_move(g);
    ++legal;
    if (v > score) {
      score = v;
      best_move = pv_move;
      if (score >= beta)
	goto endsearch;
      if (score > alpha) {
	search_pv = 0;
	alpha = score;
	update_pvline(depth, pv_move);
      }
    }
  }

  // 6) Null move.
  if (!check &&
      search_pv && depth > 0 &&
      depth + 3 < maxdepth &&
      null_move_enabled(g) &&
      beta < MATE_VAL(0)) {  // !!!! ?????
      STAT_REPORT(stat_null_tests);
    game_make_move(g, NULL_MOVE);
    int v = -absearch(g, -beta, -beta + 1, depth + 1, maxdepth - 2, 0);
    game_undo_move(g);
    if (v >= beta) {
      STAT_REPORT(stat_null_pruning);
      return beta;
    }
  }

  // 7) Probe the hash table.
  if (ttable_probe(pos, &ttentry)) {
    table_move = ttentry.best;
    // 7a) If the entry is deep enough then try to return the score.
    if (!inpv && depth > 0 && ttentry.depth >= init_maxdepth - depth) {
      int flags = ttentry.flags;
      int score = ttentry.score;
      if (IS_MATE_VAL(score)) {
	int d = MATE_DEPTH(score) + depth;
	if (d > MAX_MATE_DEPTH)
	  d = MAX_MATE_DEPTH;
	score = MATE_VAL(d);
      } else if (IS_MATE_VAL(-score)) {
	int d = MATE_DEPTH(-score) + depth;
	if (d > MAX_MATE_DEPTH)
	  d = MAX_MATE_DEPTH;
	score = -MATE_VAL(d);
      }
      if (flags == TT_EXACT) {
	STAT_REPORT(stat_tt_nodes);
      	return score;
      }
      if (flags == TT_BETA && score >= beta) {
	STAT_REPORT(stat_tt_nodes);
      	return score;
      }
      if (flags == TT_ALPHA && score <= init_alpha) {
	STAT_REPORT(stat_tt_nodes);
      	return score;
      }
    }
  }

  // 7b) Internal iterative deepening based on TT.
  if (init_maxdepth - depth > 3 && 
      (table_move == NULL_MOVE || 
       ttentry.depth < init_maxdepth - depth - 3)) {
    int tmplen = pvlen[depth];
    move_t tmpline[MAX_PV_LINE_LEN];
    memcpy(tmpline, pvlines[depth], sizeof(move_t) * tmplen);
    absearch(g, alpha, beta, depth, init_maxdepth - 1, 0);
    STAT_REPORT(stat_iid_searches);
    pvlen[depth] = tmplen;
    memcpy(pvlines[depth], tmpline, sizeof(move_t) * tmplen);
    if (ttable_probe(pos, &ttentry))
      table_move = ttentry.best;
  }

  // 8) Try the move from the transposition table.
  if (table_move == pv_move)
    table_move = NULL_MOVE;
  if (table_move != NULL_MOVE) {
    if (movegen_is_pseudo_legal(pos, table_move) &&
	movegen_verify_move(pos, table_move)) {
      
      game_make_move(g, table_move);
      int v = -absearch(g, -beta, -alpha, depth + 1, maxdepth, 0);
      game_undo_move(g);
      ++legal;
      if (v > score) {
	score = v;
	best_move = table_move;
	if (score >= beta) {
	  STAT_REPORT(stat_tt_beta_nodes);
	  goto endsearch;
	}
	if (score > alpha) {
	  search_pv = 0;
	  alpha = score;
	  update_pvline(depth, table_move);
	}
      }
    }
  }

  // #) Internal iterative deepening.
#if 0
  if (0 && init_maxdepth - depth > 3 //) {
      && pv_move == NULL_MOVE &&
      table_move == NULL_MOVE) {
    int tmplen = pvlen[depth];
    move_t tmpline[MAX_PV_LINE_LEN];
    memcpy(tmpline, pvlines[depth], sizeof(move_t) * tmplen);
    absearch(g, alpha, beta, depth, init_maxdepth - 2, 0);
    STAT_REPORT(stat_iid_searches);
    if (pvlen[depth] > 0 &&
	pvlines[depth][0] != pv_move &&
	pvlines[depth][0] != table_move)
      iid_move = pvlines[depth][0];
    else
      iid_move = NULL_MOVE;
    pvlen[depth] = tmplen;
    memcpy(pvlines[depth], tmpline, sizeof(move_t) * tmplen);
    if (iid_move != NULL_MOVE) {
      assert(movegen_is_pseudo_legal(pos, iid_move));
      assert(movegen_verify_move(pos, iid_move));
      game_make_move(g, iid_move);
      int v = -absearch(g, -beta, -alpha, depth + 1, maxdepth, 0);
      game_undo_move(g);
      ++legal;
      if (v > score) {
	score = v;
	best_move = iid_move;
	if (score >= beta) {
	  STAT_REPORT(stat_iid_beta_nodes);
	  goto endsearch;
	}
	if (score > alpha) {
	  search_pv = 0;
	  alpha = score;
	  update_pvline(depth, iid_move);
	}
      }
    }
  }
#endif

  // 9) Generate the moves.
  move_t movebuf[MAX_N_MOVES];
  int n;
  if (check) {
    n = movegen_check_evasion(pos, movebuf);
  } else {
    n = movegen_moves_nocheck(pos, movebuf);
  }

  // 10) Postprocessing of the generated moves.
  if (table_move != NULL_MOVE)
    n = delete_move(table_move, movebuf, n);
  if (pv_move != NULL_MOVE)
    n = delete_move(pv_move, movebuf, n);
  if (iid_move != NULL_MOVE)
    n = delete_move(iid_move, movebuf, n);
  if (depth == 0) {
    evaluate_moves_qsearch(g, movebuf, n);
  } else {
    if (maxdepth - depth > 2)
      evaluate_moves_see(pos, movebuf, n);
    else
      evaluate_moves(pos, movebuf, n);
    evaluate_killers(depth, movebuf, n);
  }

  // 11) Try all the moves.
  for (int i = 0; i < n; i++) {
    pop_best_move(movebuf + i, n - i);
    assert(movegen_is_pseudo_legal(pos, movebuf[i]));
    if (!movegen_verify_move(pos, movebuf[i]))
      continue;

    ++legal;
    game_make_move(g, movebuf[i]);
    int v;
    int reduction = 0;
    if (!check &&
	!inpv &&
	legal > 3 &&
	depth < maxdepth - 3 &&
	game_last_capture(g) == EMPTY &&
	MOVE_PROMOTION(movebuf[i]) == NO_PROMOTION) {
      STAT_REPORT(stat_lmr_moves);
      reduction = 1;  // Late Move Reduction
    }

    if (search_pv) {
      v = -absearch(g, -beta, -alpha, depth + 1, maxdepth - reduction, 0);
      if (v > alpha && reduction > 0)
	v = -absearch(g, -beta, -alpha, depth + 1, maxdepth, 0);
    } else {
      v = -absearch(g, -alpha - 1, -alpha, depth + 1, maxdepth - reduction, 0);
      if (v > alpha && beta != alpha + 1)
	v = -absearch(g, -beta, -alpha, depth + 1, maxdepth, 0);
    }
    game_undo_move(g);
    if (v <= score)
      continue;
    score = v;
    best_move = movebuf[i];
    if (score >= beta)
      break;
    if (score > alpha) {
      search_pv = 0;
      alpha = score;
      update_pvline(depth, movebuf[i]);
    }
  }

  // 12) Detect checkmate and stalemate and return.
  if (legal == 0) {
    STAT_REPORT(stat_interior_nodes);
    score = (check ? -MATE_VAL(depth) : 0);
  }

  // 13) Update the killer moves.
  if (score >= beta && best_move != killers[depth][0]) {
    killers[depth][1] = killers[depth][0];
    killers[depth][0] = best_move;
  }

 endsearch:

  if (score < beta) {
    if (best_move == pv_move)
      STAT_REPORT(stat_pv_best);
    else if (best_move == table_move)
      STAT_REPORT(stat_tt_best);
    else if (best_move == iid_move)
      STAT_REPORT(stat_iid_best);
  }
		
  // 14) Add an entry to the transposition table.
  add_ttable_entry(pos, best_move, score, depth,
		   init_maxdepth,init_alpha, beta);

  if (score <= init_alpha)
    STAT_REPORT(stat_alpha_nodes);
  else if (score >= beta)
    STAT_REPORT(stat_beta_nodes);
  else
    STAT_REPORT(stat_pv_nodes);
  
  assert(stop_flag != cont_flag || abs(score) <= MATE_VAL(0));

  return score;
}


static move_t iterative_search(struct game_t* g, int maxdepth, int* retscore)
{
  struct timeval start;
  struct timeval end;
  /* An array big enough to contain the text of the pv, including one
     char separator and the string terminator. */
  char movestr[MAX_PV_LINE_LEN * MAX_SAN_LEN];

  int mindepth = 2;
  if (mindepth > maxdepth)
    mindepth = maxdepth;

  move_t m = NULL_MOVE;
  int gamelen = game_length(g);
  oldpvlen = 0;
  int low = -INF_VAL;
  int high = INF_VAL;

  eval_reset_max_positional_value();

  for (int depth = mindepth; depth <= maxdepth; depth++) {
    int score;
    for (;;) {
      pvlen[0] = 0;
      nodes = 0;

      gettimeofday(&start, 0);
      if (depth == mindepth || setjmp(jmp_stopsearch) == 0) {
	// Start the search.
	score = absearch(g, low, high, 0, depth, 1);
      } else {
	// Search aborted.
	while (game_length(g) > gamelen)
	  game_undo_move(g);
	break;
      }
      gettimeofday(&end, 0);


      eval_update_max_positional_value();
      *retscore = score;
      oldpvlen = pvlen[0];
      memcpy(oldpv, pvlines[0], pvlen[0] * sizeof(move_t));

      long time = (end.tv_sec - start.tv_sec) * 100;
      time += (end.tv_usec - start.tv_usec) / 10000;

      if (interface_ptr->show_think) {
	printf("%-2d %-+4d %-4ld %-8" PRIu64, depth, score, time, nodes);
	printf(" %s\n", moves_to_san_string(pvlines[0],
					    pvlen[0],
					    " ",
					    g,
					    movestr));
      }
      if (score <= low)
	low = -INF_VAL;
      else if (score >= high)
	high = INF_VAL;
      else
	break;
    }
    if (pvlen[0] > 0) {
      m = pvlines[0][0];
      if (IS_MATE_VAL(score))
	break;
    }
    if (stop_flag != cont_flag)
      break;
    low = score - ASPIRATION_WINDOW;
    high = score + ASPIRATION_WINDOW;
  }

  return (stop_flag == cancel_flag ? NULL_MOVE : m);
}


void search_cancel()
{
  stop_flag = cancel_flag;
}


void search_hurry()
{
  stop_flag = hurry_flag;
}


move_t search_start(interface_t* iface)
{
  assert(iface != 0);

  iface->searching = 1;
  interface_ptr = iface;
  stop_flag = cont_flag;

  move_t m = book_move(&(iface->game));
  if (m != NULL_MOVE) {
    char buf[MAX_SAN_LEN];
    move_to_san_string(m, game_cur_position(&(iface->game)), buf);
    if (iface->show_think)
      printf(" 0 0 0 0 (book: %s)\n", buf);
    printf("move %s\n", buf);
    if (!game_make_move(&(iface->game), m)) {
      printf("Error (internal): move execution\n");
      iface->state = is_force;
    }
    if (iface->tc_moves > 0)
      --iface->tc_moves;
    iface->searching = 0;
    return m;
  }

  int depth = iface->depth_limit;
  if (depth == 0 || depth > MAX_DEPTH)
    depth = MAX_DEPTH;

  int maxtime = iface->time_limit;
  if (maxtime == 0 || maxtime > MAX_TIME)
    maxtime = MAX_TIME;

  int moves = (iface->tc_moves > 0 ? iface->tc_moves : 20);
  int time = iface->tc_time;
  time += iface->increment * (moves > 3 ? moves - 3 : 0);
  time /= moves;

  if (time > maxtime)
    time = maxtime;

  gettimeofday(&timeout, 0);
  timeout.tv_sec += time / 1000;
  timeout.tv_usec -= 10000;  // substract a safety term (100 msec)
  timeout.tv_usec += time % 1000;
  if (timeout.tv_usec < 0) {
    timeout.tv_usec += 1000000;
    timeout.tv_sec -= 1;
  }
  if (timeout.tv_usec >= 1000000) {
    timeout.tv_usec -= 1000000;
    timeout.tv_sec += 1;
  }

  ttable_reset();
  int score;
  

  m = iterative_search(&(iface->game), depth, &score);
  if (m != NULL_MOVE) {
    if (score < -RESIGNATION_SCORE) {
      if (++(iface->resignation_count) >= RESIGNATION_MOVES) {
	printf("resign\n");
	iface->resignation_count = 0;
      }
    } else {
      iface->resignation_count = 0;
    }
    char buf[MAX_SAN_LEN];
    move_to_san_string(m, game_cur_position(&(iface->game)), buf);
    printf("move %s\n", buf);
    if (!game_make_move(&(iface->game), m)) {
      printf("Error (internal): move execution\n");
      iface->state = is_force;
    }
    if (iface->tc_moves > 0)
      --iface->tc_moves;
  } else {
    printf("Error (internal): no move generated\n");
    printf("resign\n");
  }

  iface->searching = 0;

  return m;
}
